package websocket

import (
	"encoding/json"
	"fmt"
	"gitee.com/jn-qq/go-tools/slices"
	"github.com/gorilla/websocket"
	"maps"
	"regexp"
	"strconv"
	"strings"
	"time"
	"vector/control/request"
)

type CustomerService struct {
	topString           string
	knowledgeListString string
	question            string
	resultData          map[string]any
}

func (c *CustomerService) NewTaskData(row map[string]any) error {
	// 复制任务数据避免修改原数据
	c.resultData = maps.Clone(row)

	requiredFields := map[string]*string{
		"问题":     &c.question,
		"知识":     &c.knowledgeListString,
		"TOP 10": &c.topString,
	}

	// 统一处理必填字段
	for field, target := range requiredFields {
		if value, ok := row[field]; ok {
			*target = value.(string)
		} else {
			errMsg := fmt.Sprintf("%s参数不能为空", field)
			c.resultData["备注"] = errMsg
			return fmt.Errorf(errMsg)
		}
	}

	return nil
}

func (c *CustomerService) Do() {

	// 解析top字符串
	iflytek := new(request.IflytekVector)
	if err := iflytek.TopStringToXfData(c.topString, ""); err != nil {
		c.resultData["备注"] = "解析topString异常:" + err.Error()
		return
	}

	var simpleTop []string
	var contentBuilder strings.Builder
	for i, item := range iflytek.Data {
		simpleTop = append(simpleTop, fmt.Sprintf("知识%d：%s：%s", i+1, item.KnowledgeName, item.GroupName))
		item.Content = strings.ReplaceAll(item.Content, "\\n", "\n")
		item.Content = strings.ReplaceAll(item.Content, "\\", "\"")

		if contentBuilder.Len() > 0 {
			contentBuilder.WriteString("<ret>")
		}
		contentBuilder.WriteString(fmt.Sprintf("知识%d：%s：%s：%s", i+1, item.KnowledgeName, item.GroupName, item.Content))
	}

	if len(simpleTop) == 0 {
		c.resultData["备注"] = "没有找到知识"
		return
	}

	// 限定content里字符串总长度为1万
	content := splitLen(10000, contentBuilder.String())

	// 格式化标准答案
	knowledgeList := request.KnowledgeFormat(c.knowledgeListString, "", true)

	// 构建请求参数
	// 构建请求负载body
	body, err := c.payload(content)
	if err != nil {
		c.resultData["备注"] = "ws入参encoding异常:" + err.Error()
		return
	}

	startTime := time.Now()

	// 构建请求
	conn, _, err := websocket.DefaultDialer.Dial("ws://172.29.100.69:9969/turing/v3/gpt", nil)
	if err != nil {
		c.resultData["备注"] = "ws链接异常:" + err.Error()
		return
	}
	defer conn.Close()

	// 发送请求
	if err := conn.WriteMessage(websocket.TextMessage, body); err != nil {
		c.resultData["备注"] = "ws发送消息异常:" + err.Error()
		return
	}
	var resp strings.Builder
	// 获取响应
	for {
		_, message, err := conn.ReadMessage()
		if err != nil {
			c.resultData["备注"] = "ws接收消息异常:" + err.Error()
			return
		}
		// 解析json响应
		var res map[string]any
		if err := json.Unmarshal(message, &res); err != nil {
			c.resultData["备注"] = "ws消息解析异常:" + err.Error()
			return
		}

		payload, ok := res["payload"].(map[string]any)
		if !ok {
			c.resultData["备注"] = "ws消息格式异常: payload类型错误"
			return
		}

		if payload["choices"] == nil {
			if header, ok := res["header"].(map[string]any); ok {
				if msg, ok := header["message"].(string); ok {
					c.resultData["备注"] = msg
				} else {
					c.resultData["备注"] = "ws响应header.message字段类型错误"
				}
			} else {
				c.resultData["备注"] = "ws响应header字段类型错误"
			}
			return
		}

		choices, ok := payload["choices"].(map[string]any)
		if !ok {
			c.resultData["备注"] = "ws消息格式异常: choices类型错误"
			return
		}

		text, ok := choices["text"].([]any)
		if !ok || len(text) == 0 {
			c.resultData["备注"] = "ws消息格式异常: text类型错误或为空"
			return
		}

		firstText, ok := text[0].(map[string]any)
		if !ok {
			c.resultData["备注"] = "ws消息格式异常: text[0]类型错误"
			return
		}

		contentVal, ok := firstText["content"].(string)
		if !ok {
			c.resultData["备注"] = "ws消息格式异常: content类型错误"
			return
		}

		resp.WriteString(contentVal)

		status, ok := choices["status"].(float64)
		if !ok {
			c.resultData["备注"] = "ws消息格式异常: status类型错误"
			return
		}

		if status == 2 {
			break
		}
	}
	spendTime := time.Since(startTime).Milliseconds()
	c.resultData["命中知识"] = ""
	c.resultData["是否准确"] = false

	response := resp.String()
	if response != "" {
		// 正则表达式匹配 结果
		re := regexp.MustCompile(`【知识(\d+)】`)
		// 查找所有匹配项
		matches := re.FindAllStringSubmatch(response, -1)
		// 提取结果
		var results []string

		for _, match := range matches {
			if len(match) > 1 {
				index, err := strconv.Atoi(match[1])
				if err != nil {
					continue
				}
				results = append(results, fmt.Sprintf("【知识%d】", index))
				if index > 0 && index <= len(iflytek.Data) {
					if atoms, ok := knowledgeList[iflytek.Data[index-1].KnowledgeName]; ok &&
						slices.Contains(atoms, iflytek.Data[index-1].GroupName) &&
						!c.resultData["是否准确"].(bool) {
						c.resultData["是否准确"] = true
					}
				}
			}
		}
		c.resultData["命中知识"] = strings.Join(results, "\n")
	}
	c.resultData["简单TOP"] = strings.Join(simpleTop, "\n")
	c.resultData["响应结果"] = response
	c.resultData["响应耗时"] = spendTime
}

func (c *CustomerService) Result() map[string]any {
	return maps.Clone(c.resultData)
}

func (c *CustomerService) Reset() {
	c.topString = ""
	c.knowledgeListString = ""
	c.question = ""
	clear(c.resultData)
}

func (c *CustomerService) payload(content []string) ([]byte, error) {
	// 构建请求参数
	contentStr := strings.Join(content, "<ret>")
	question := c.question

	// 构建消息内容
	messageContent := fmt.Sprintf(
		"<User> 你是一个文档问答专家，请严格根据提供的知识片段进行用户问题回答，禁止任何主观加工或补充。请按以下步骤处理：<ret># 任务要求<ret>1. 问题分类（必须三选一）：<ret>- [Reply] 知识中有直接对应的答案段落<ret>- [Reject] 知识中完全无相关内容<ret>- [Confuse] 问题表述模糊需澄清（如范围过广、存在歧义）<ret>2. 溯源判断（严格标准）：<ret>- 仅当知识片段包含与问题完全匹配的表述时标记1<ret>- 每个知识单独判断，格式：知识1:1/0, 知识2:1/0...<ret>3. 回复标准：<ret>- 必须显式标注引用的知识片段ID回复格式为：【知识ID】<ret>- 绝对禁止转述/总结/润色/抽取内容<ret>#可参考知识如下：<ret>%s<ret>用户输入问题：%s<ret># 输出模板（严格遵循）<ret># Type [分类标签]<ret># Trace 知识1:1, 知识2:0...<ret># Response【知识ID】<ret><end><Bot>",
		contentStr,
		question,
	)

	payload := map[string]any{
		"header": map[string]string{
			"app_id": "2d7632f6",
			"uid":    "1234",
		},
		"parameter": map[string]any{
			"chat": map[string]any{
				"domain":              "x1",
				"temperature":         0.6,
				"max_tokens":          32768,
				"auditing":            "default",
				"no_audit_qr":         true,
				"repeat_punish":       2.01,
				"repeat_punish_scale": 0.001,
				"top_p":               0.01,
				"tools": []map[string]any{
					{
						"type": "web_search",
						"web_search": map[string]any{
							"enable":         true,
							"show_ref_label": true,
							"search_mode":    "deep",
						},
					},
				},
			},
		},
		"payload": map[string]any{
			"message": map[string]any{
				"text": []map[string]string{
					{
						"role":    "user",
						"content": messageContent,
					},
				},
			},
		},
	}
	return json.Marshal(payload)
}

func splitLen(n int, s ...string) []string {
	if len(s) == 0 {
		return s
	}

	var total int
	for _, v := range s {
		total += len(v)
	}
	if total <= n {
		// 避免修改原切片
		result := make([]string, len(s))
		copy(result, s)
		return result
	}

	result := make([]string, len(s))
	copy(result, s)

	remaining := n
	for i := range result {
		if remaining <= 0 {
			result[i] = ""
			continue
		}
		if len(result[i]) > remaining {
			result[i] = result[i][:remaining]
		}
		remaining -= len(result[i])
	}

	return result
}
